home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / lib / parsedate.y < prev    next >
Text File  |  1993-03-18  |  22KB  |  827 lines

  1. %{
  2. /* $Revision: 1.13 $
  3. **
  4. **  Originally written by Steven M. Bellovin <smb@research.att.com> while
  5. **  at the University of North Carolina at Chapel Hill.  Later tweaked by
  6. **  a couple of people on Usenet.  Completely overhauled by Rich $alz
  7. **  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
  8. **  Further revised (removed obsolete constructs and cleaned up timezone
  9. **  names) in August, 1991, by Rich.  Paul Eggert <eggert@twinsun.com>
  10. **  helped in September, 1992.
  11. **
  12. **  This grammar has six shift/reduce conflicts.
  13. **
  14. **  This code is in the public domain and has no copyright.
  15. */
  16. /* SUPPRESS 530 *//* Empty body for statement */
  17. /* SUPPRESS 593 on yyerrlab *//* Label was not used */
  18. /* SUPPRESS 593 on yynewstate *//* Label was not used */
  19. /* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */
  20. #include "configdata.h"
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <sys/types.h>
  24. #if    defined(DO_NEED_TIME)
  25. #include <time.h>
  26. #endif    /* defined(DO_NEED_TIME) */
  27. #include <sys/time.h>
  28. #include "libinn.h"
  29. #include "macros.h"
  30.  
  31.  
  32. #define yyparse        date_parse
  33. #define yylex        date_lex
  34. #define yyerror        date_error
  35.  
  36.  
  37.     /* See the LeapYears table in Convert. */
  38. #define EPOCH        1970
  39. #define END_OF_TIME    2038
  40.     /* Constants for general time calculations. */
  41. #define DST_OFFSET    1
  42. #define SECSPERDAY    (24L * 60L * 60L)
  43.     /* Readability for TABLE stuff. */
  44. #define HOUR(x)        (x * 60)
  45.  
  46. #define LPAREN        '('
  47. #define RPAREN        ')'
  48. #define IS7BIT(x)    ((unsigned int)(x) < 0200)
  49.  
  50.  
  51. /*
  52. **  An entry in the lexical lookup table.
  53. */
  54. typedef struct _TABLE {
  55.     STRING    name;
  56.     int        type;
  57.     time_t    value;
  58. } TABLE;
  59.  
  60. /*
  61. **  Daylight-savings mode:  on, off, or not yet known.
  62. */
  63. typedef enum _DSTMODE {
  64.     DSTon, DSToff, DSTmaybe
  65. } DSTMODE;
  66.  
  67. /*
  68. **  Meridian:  am, pm, or 24-hour style.
  69. */
  70. typedef enum _MERIDIAN {
  71.     MERam, MERpm, MER24
  72. } MERIDIAN;
  73.  
  74.  
  75. /*
  76. **  Global variables.  We could get rid of most of them by using a yacc
  77. **  union, but this is more efficient.  (This routine predates the
  78. **  yacc %union construct.)
  79. */
  80. static char    *yyInput;
  81. static DSTMODE    yyDSTmode;
  82. static int    yyHaveDate;
  83. static int    yyHaveRel;
  84. static int    yyHaveTime;
  85. static time_t    yyTimezone;
  86. static time_t    yyDay;
  87. static time_t    yyHour;
  88. static time_t    yyMinutes;
  89. static time_t    yyMonth;
  90. static time_t    yySeconds;
  91. static time_t    yyYear;
  92. static MERIDIAN    yyMeridian;
  93. static time_t    yyRelMonth;
  94. static time_t    yyRelSeconds;
  95.  
  96.  
  97. extern struct tm    *localtime();
  98.  
  99. static void        date_error();
  100. %}
  101.  
  102. %union {
  103.     time_t        Number;
  104.     enum _MERIDIAN    Meridian;
  105. }
  106.  
  107. %token    tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
  108. %token    tUNUMBER tZONE
  109.  
  110. %type    <Number>    tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
  111. %type    <Number>    tSNUMBER tUNUMBER tZONE numzone zone
  112. %type    <Meridian>    tMERIDIAN o_merid
  113.  
  114. %%
  115.  
  116. spec    : /* NULL */
  117.     | spec item
  118.     ;
  119.  
  120. item    : time {
  121.         yyHaveTime++;
  122. #if    defined(lint)
  123.         /* I am compulsive about lint natterings... */
  124.         if (yyHaveTime == -1) {
  125.         YYERROR;
  126.         }
  127. #endif    /* defined(lint) */
  128.     }
  129.     | time zone {
  130.         yyHaveTime++;
  131.         yyTimezone = $2;
  132.     }
  133.     | date {
  134.         yyHaveDate++;
  135.     }
  136.     | rel {
  137.         yyHaveRel = 1;
  138.     }
  139.     ;
  140.  
  141. time    : tUNUMBER o_merid {
  142.         if ($1 < 100) {
  143.         yyHour = $1;
  144.         yyMinutes = 0;
  145.         }
  146.         else {
  147.         yyHour = $1 / 100;
  148.         yyMinutes = $1 % 100;
  149.         }
  150.         yySeconds = 0;
  151.         yyMeridian = $2;
  152.     }
  153.     | tUNUMBER ':' tUNUMBER o_merid {
  154.         yyHour = $1;
  155.         yyMinutes = $3;
  156.         yySeconds = 0;
  157.         yyMeridian = $4;
  158.     }
  159.     | tUNUMBER ':' tUNUMBER numzone {
  160.         yyHour = $1;
  161.         yyMinutes = $3;
  162.         yyTimezone = $4;
  163.         yyMeridian = MER24;
  164.         yyDSTmode = DSToff;
  165.     }
  166.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
  167.         yyHour = $1;
  168.         yyMinutes = $3;
  169.         yySeconds = $5;
  170.         yyMeridian = $6;
  171.     }
  172.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
  173.         yyHour = $1;
  174.         yyMinutes = $3;
  175.         yySeconds = $5;
  176.         yyTimezone = $6;
  177.         yyMeridian = MER24;
  178.         yyDSTmode = DSToff;
  179.     }
  180.     ;
  181.  
  182. zone    : tZONE {
  183.         $$ = $1;
  184.         yyDSTmode = DSToff;
  185.     }
  186.     | tDAYZONE {
  187.         $$ = $1;
  188.         yyDSTmode = DSTon;
  189.     }
  190.     | tZONE numzone {
  191.         /* Only allow "GMT+300" and "GMT-0800" */
  192.         if ($1 != 0) {
  193.         YYABORT;
  194.         }
  195.         $$ = $2;
  196.         yyDSTmode = DSToff;
  197.     }
  198.     | numzone {
  199.         $$ = $1;
  200.         yyDSTmode = DSToff;
  201.     }
  202.     ;
  203.  
  204. numzone    : tSNUMBER {
  205.         int        i;
  206.  
  207.         /* Unix and GMT and numeric timezones -- a little confusing. */
  208.         if ($1 < 0) {
  209.         /* Don't work with negative modulus. */
  210.         $1 = -$1;
  211.         if ($1 > 9999 || (i = $1 % 100) >= 60) {
  212.             YYABORT;
  213.         }
  214.         $$ = ($1 / 100) * 60 + i;
  215.         }
  216.         else {
  217.         if ($1 > 9999 || (i = $1 % 100) >= 60) {
  218.             YYABORT;
  219.         }
  220.         $$ = -(($1 / 100) * 60 + i);
  221.         }
  222.     }
  223.     ;
  224.  
  225. date    : tUNUMBER '/' tUNUMBER {
  226.         yyMonth = $1;
  227.         yyDay = $3;
  228.     }
  229.     | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
  230.         if ($1 > 100) {
  231.         yyYear = $1;
  232.         yyMonth = $3;
  233.         yyDay = $5;
  234.         }
  235.         else {
  236.         yyMonth = $1;
  237.         yyDay = $3;
  238.         yyYear = $5;
  239.         }
  240.     }
  241.     | tMONTH tUNUMBER {
  242.         yyMonth = $1;
  243.         yyDay = $2;
  244.     }
  245.     | tMONTH tUNUMBER ',' tUNUMBER {
  246.         yyMonth = $1;
  247.         yyDay = $2;
  248.         yyYear = $4;
  249.     }
  250.     | tUNUMBER tMONTH {
  251.         yyDay = $1;
  252.         yyMonth = $2;
  253.     }
  254.     | tUNUMBER tMONTH tUNUMBER {
  255.         yyDay = $1;
  256.         yyMonth = $2;
  257.         yyYear = $3;
  258.     }
  259.     | tDAY ',' tUNUMBER tMONTH tUNUMBER {
  260.         yyDay = $3;
  261.         yyMonth = $4;
  262.         yyYear = $5;
  263.     }
  264.     ;
  265.  
  266. rel    : tSNUMBER tSEC_UNIT {
  267.         yyRelSeconds += $1 * $2;
  268.     }
  269.     | tUNUMBER tSEC_UNIT {
  270.         yyRelSeconds += $1 * $2;
  271.     }
  272.     | tSNUMBER tMONTH_UNIT {
  273.         yyRelMonth += $1 * $2;
  274.     }
  275.     | tUNUMBER tMONTH_UNIT {
  276.         yyRelMonth += $1 * $2;
  277.     }
  278.     ;
  279.  
  280. o_merid    : /* NULL */ {
  281.         $$ = MER24;
  282.     }
  283.     | tMERIDIAN {
  284.         $$ = $1;
  285.     }
  286.     ;
  287.  
  288. %%
  289.  
  290. /* Month and day table. */
  291. static TABLE    MonthDayTable[] = {
  292.     { "january",    tMONTH,  1 },
  293.     { "february",    tMONTH,  2 },
  294.     { "march",        tMONTH,  3 },
  295.     { "april",        tMONTH,  4 },
  296.     { "may",        tMONTH,  5 },
  297.     { "june",        tMONTH,  6 },
  298.     { "july",        tMONTH,  7 },
  299.     { "august",        tMONTH,  8 },
  300.     { "september",    tMONTH,  9 },
  301.     { "october",    tMONTH, 10 },
  302.     { "november",    tMONTH, 11 },
  303.     { "december",    tMONTH, 12 },
  304.     /* The value of the day isn't used... */
  305.     { "sunday",        tDAY, 0 },
  306.     { "monday",        tDAY, 0 },
  307.     { "tuesday",    tDAY, 0 },
  308.     { "wednesday",    tDAY, 0 },
  309.     { "thursday",    tDAY, 0 },
  310.     { "friday",        tDAY, 0 },
  311.     { "saturday",    tDAY, 0 },
  312. };
  313.  
  314. /* Time units table. */
  315. static TABLE    UnitsTable[] = {
  316.     { "year",        tMONTH_UNIT,    12 },
  317.     { "month",        tMONTH_UNIT,    1 },
  318.     { "week",        tSEC_UNIT,    7 * 24 * 60 * 60 },
  319.     { "day",        tSEC_UNIT,    1 * 24 * 60 * 60 },
  320.     { "hour",        tSEC_UNIT,    60 * 60 },
  321.     { "minute",        tSEC_UNIT,    60 },
  322.     { "min",        tSEC_UNIT,    60 },
  323.     { "second",        tSEC_UNIT,    1 },
  324.     { "sec",        tSEC_UNIT,    1 },
  325. };
  326.  
  327. /* Timezone table. */
  328. static TABLE    TimezoneTable[] = {
  329.     { "gmt",    tZONE,     HOUR( 0) },    /* Greenwich Mean */
  330.     { "ut",    tZONE,     HOUR( 0) },    /* Universal */
  331.     { "utc",    tZONE,     HOUR( 0) },    /* Universal Coordinated */
  332.     { "cut",    tZONE,     HOUR( 0) },    /* Coordinated Universal */
  333.     { "z",    tZONE,     HOUR( 0) },    /* Greenwich Mean */
  334.     { "wet",    tZONE,     HOUR( 0) },    /* Western European */
  335.     { "bst",    tDAYZONE,  HOUR( 0) },    /* British Summer */
  336.     { "nst",    tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
  337.     { "ndt",    tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
  338.     { "ast",    tZONE,     HOUR( 4) },    /* Atlantic Standard */
  339.     { "adt",    tDAYZONE,  HOUR( 4) },    /* Atlantic Daylight */
  340.     { "est",    tZONE,     HOUR( 5) },    /* Eastern Standard */
  341.     { "edt",    tDAYZONE,  HOUR( 5) },    /* Eastern Daylight */
  342.     { "cst",    tZONE,     HOUR( 6) },    /* Central Standard */
  343.     { "cdt",    tDAYZONE,  HOUR( 6) },    /* Central Daylight */
  344.     { "mst",    tZONE,     HOUR( 7) },    /* Mountain Standard */
  345.     { "mdt",    tDAYZONE,  HOUR( 7) },    /* Mountain Daylight */
  346.     { "pst",    tZONE,     HOUR( 8) },    /* Pacific Standard */
  347.     { "pdt",    tDAYZONE,  HOUR( 8) },    /* Pacific Daylight */
  348.     { "yst",    tZONE,     HOUR( 9) },    /* Yukon Standard */
  349.     { "ydt",    tDAYZONE,  HOUR( 9) },    /* Yukon Daylight */
  350.     { "akst",    tZONE,     HOUR( 9) },    /* Alaska Standard */
  351.     { "akdt",    tDAYZONE,  HOUR( 9) },    /* Alaska Daylight */
  352.     { "hst",    tZONE,     HOUR(10) },    /* Hawaii Standard */
  353.     { "hast",    tZONE,     HOUR(10) },    /* Hawaii-Aleutian Standard */
  354.     { "hadt",    tDAYZONE,  HOUR(10) },    /* Hawaii-Aleutian Daylight */
  355.     { "ces",    tDAYZONE,  -HOUR(1) },    /* Central European Summer */
  356.     { "cest",    tDAYZONE,  -HOUR(1) },    /* Central European Summer */
  357.     { "mez",    tZONE,     -HOUR(1) },    /* Middle European */
  358.     { "mezt",    tDAYZONE,  -HOUR(1) },    /* Middle European Summer */
  359.     { "cet",    tZONE,     -HOUR(1) },    /* Central European */
  360.     { "met",    tZONE,     -HOUR(1) },    /* Middle European */
  361.     { "eet",    tZONE,     -HOUR(2) },    /* Eastern Europe */
  362.     { "msk",    tZONE,     -HOUR(3) },    /* Moscow Winter */
  363.     { "msd",    tDAYZONE,  -HOUR(3) },    /* Moscow Summer */
  364.     { "wast",    tZONE,     -HOUR(8) },    /* West Australian Standard */
  365.     { "wadt",    tDAYZONE,  -HOUR(8) },    /* West Australian Daylight */
  366.     { "hkt",    tZONE,     -HOUR(8) },    /* Hong Kong */
  367.     { "cct",    tZONE,     -HOUR(8) },    /* China Coast */
  368.     { "jst",    tZONE,     -HOUR(9) },    /* Japan Standard */
  369.     { "kst",    tZONE,     -HOUR(9) },    /* Korean Standard */
  370.     { "kdt",    tZONE,     -HOUR(9) },    /* Korean Daylight */
  371.     { "cast",    tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
  372.     { "cadt",    tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
  373.     { "east",    tZONE,     -HOUR(10) },    /* Eastern Australian Standard */
  374.     { "eadt",    tDAYZONE,  -HOUR(10) },    /* Eastern Australian Daylight */
  375.     { "nzst",    tZONE,     -HOUR(12) },    /* New Zealand Standard */
  376.     { "nzdt",    tDAYZONE,  -HOUR(12) },    /* New Zealand Daylight */
  377.  
  378.     /* For completeness we include the following entries. */
  379. #if    0
  380.  
  381.     /* Duplicate names.  Either they conflict with a zone listed above
  382.      * (which is either more likely to be seen or just been in circulation
  383.      * longer), or they conflict with another zone in this section and
  384.      * we could not reasonably choose one over the other. */
  385.     { "fst",    tZONE,     HOUR( 2) },    /* Fernando De Noronha Standard */
  386.     { "fdt",    tDAYZONE,  HOUR( 2) },    /* Fernando De Noronha Daylight */
  387.     { "bst",    tZONE,     HOUR( 3) },    /* Brazil Standard */
  388.     { "est",    tZONE,     HOUR( 3) },    /* Eastern Standard (Brazil) */
  389.     { "edt",    tDAYZONE,  HOUR( 3) },    /* Eastern Daylight (Brazil) */
  390.     { "wst",    tZONE,     HOUR( 4) },    /* Western Standard (Brazil) */
  391.     { "wdt",    tDAYZONE,  HOUR( 4) },    /* Western Daylight (Brazil) */
  392.     { "cst",    tZONE,     HOUR( 5) },    /* Chile Standard */
  393.     { "cdt",    tDAYZONE,  HOUR( 5) },    /* Chile Daylight */
  394.     { "ast",    tZONE,     HOUR( 5) },    /* Acre Standard */
  395.     { "adt",    tDAYZONE,  HOUR( 5) },    /* Acre Daylight */
  396.     { "cst",    tZONE,     HOUR( 5) },    /* Cuba Standard */
  397.     { "cdt",    tDAYZONE,  HOUR( 5) },    /* Cuba Daylight */
  398.     { "est",    tZONE,     HOUR( 6) },    /* Easter Island Standard */
  399.     { "edt",    tDAYZONE,  HOUR( 6) },    /* Easter Island Daylight */
  400.     { "sst",    tZONE,     HOUR(11) },    /* Samoa Standard */
  401.     { "ist",    tZONE,     -HOUR(2) },    /* Israel Standard */
  402.     { "idt",    tDAYZONE,  -HOUR(2) },    /* Israel Daylight */
  403.     { "idt",    tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
  404.     { "ist",    tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
  405.     { "cst",     tZONE,     -HOUR(8) },    /* China Standard */
  406.     { "cdt",     tDAYZONE,  -HOUR(8) },    /* China Daylight */
  407.     { "sst",     tZONE,     -HOUR(8) },    /* Singapore Standard */
  408.  
  409.     /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
  410.     { "gst",    tZONE,     HOUR( 3) },    /* Greenland Standard */
  411.     { "wat",    tZONE,     -HOUR(1) },    /* West Africa */
  412.     { "at",    tZONE,     HOUR( 2) },    /* Azores */
  413.     { "gst",    tZONE,     -HOUR(10) },    /* Guam Standard */
  414.     { "nft",    tZONE,     HOUR(3)+30 }, /* Newfoundland */
  415.     { "idlw",    tZONE,     HOUR(12) },    /* International Date Line West */
  416.     { "mewt",    tZONE,     -HOUR(1) },    /* Middle European Winter */
  417.     { "mest",    tDAYZONE,  -HOUR(1) },    /* Middle European Summer */
  418.     { "swt",    tZONE,     -HOUR(1) },    /* Swedish Winter */
  419.     { "sst",    tDAYZONE,  -HOUR(1) },    /* Swedish Summer */
  420.     { "fwt",    tZONE,     -HOUR(1) },    /* French Winter */
  421.     { "fst",    tDAYZONE,  -HOUR(1) },    /* French Summer */
  422.     { "bt",    tZONE,     -HOUR(3) },    /* Baghdad */
  423.     { "it",    tZONE,     -(HOUR(3)+30) }, /* Iran */
  424.     { "zp4",    tZONE,     -HOUR(4) },    /* USSR Zone 3 */
  425.     { "zp5",    tZONE,     -HOUR(5) },    /* USSR Zone 4 */
  426.     { "ist",    tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
  427.     { "zp6",    tZONE,     -HOUR(6) },    /* USSR Zone 5 */
  428.     { "nst",    tZONE,     -HOUR(7) },    /* North Sumatra */
  429.     { "sst",    tZONE,     -HOUR(7) },    /* South Sumatra */
  430.     { "jt",    tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
  431.     { "nzt",    tZONE,     -HOUR(12) },    /* New Zealand */
  432.     { "idle",    tZONE,     -HOUR(12) },    /* International Date Line East */
  433.     { "cat",    tZONE,     HOUR(10) },    /* -- expired 1967 */
  434.     { "nt",    tZONE,     HOUR(11) },    /* -- expired 1967 */
  435.     { "ahst",    tZONE,     HOUR(10) },    /* -- expired 1983 */
  436.     { "hdt",    tDAYZONE,  HOUR(10) },    /* -- expired 1986 */
  437. #endif    /* 0 */
  438. };
  439.  
  440.  
  441.  
  442. /* ARGSUSED */
  443. static void
  444. date_error(s)
  445.     char    *s;
  446. {
  447.     /* NOTREACHED */
  448. }
  449.  
  450.  
  451. static time_t
  452. ToSeconds(Hours, Minutes, Seconds, Meridian)
  453.     time_t    Hours;
  454.     time_t    Minutes;
  455.     time_t    Seconds;
  456.     MERIDIAN    Meridian;
  457. {
  458.     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
  459.     return -1;
  460.     if (Meridian == MER24) {
  461.     if (Hours < 0 || Hours > 23)
  462.         return -1;
  463.     }
  464.     else {
  465.     if (Hours < 1 || Hours > 12)
  466.         return -1;
  467.     if (Hours == 12)
  468.         Hours = 0;
  469.     if (Meridian == MERpm)
  470.         Hours += 12;
  471.     }
  472.     return (Hours * 60L + Minutes) * 60L + Seconds;
  473. }
  474.  
  475.  
  476. static time_t
  477. Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst)
  478.     time_t    Month;
  479.     time_t    Day;
  480.     time_t    Year;
  481.     time_t    Hours;
  482.     time_t    Minutes;
  483.     time_t    Seconds;
  484.     MERIDIAN    Meridian;
  485.     DSTMODE    dst;
  486. {
  487.     static int    DaysNormal[13] = {
  488.     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  489.     };
  490.     static int    DaysLeap[13] = {
  491.     0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  492.     };
  493.     static int    LeapYears[] = {
  494.     1972, 1976, 1980, 1984, 1988, 1992, 1996,
  495.     2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
  496.     };
  497.     register int    *yp;
  498.     register int    *mp;
  499.     register time_t    Julian;
  500.     register int    i;
  501.     time_t        tod;
  502.  
  503.     if (Year < 0)
  504.     Year = -Year;
  505.     if (Year < 100)
  506.     Year += 1900;
  507.     if (Year < EPOCH)
  508.     Year += 100;
  509.     for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
  510.     if (Year == *yp) {
  511.         mp = DaysLeap;
  512.         break;
  513.     }
  514.     if (Year < EPOCH || Year > END_OF_TIME
  515.      || Month < 1 || Month > 12
  516.      /* NOSTRICT *//* conversion from long may lose accuracy */
  517.      || Day < 1 || Day > mp[(int)Month])
  518.     return -1;
  519.  
  520.     Julian = Day - 1 + (Year - EPOCH) * 365;
  521.     for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
  522.     if (Year <= *yp)
  523.         break;
  524.     for (i = 1; i < Month; i++)
  525.     Julian += *++mp;
  526.     Julian *= SECSPERDAY;
  527.     Julian += yyTimezone * 60L;
  528.     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
  529.     return -1;
  530.     Julian += tod;
  531.     tod = Julian;
  532.     if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
  533.     Julian -= DST_OFFSET * 60 * 60;
  534.     return Julian;
  535. }
  536.  
  537.  
  538. static time_t
  539. DSTcorrect(Start, Future)
  540.     time_t    Start;
  541.     time_t    Future;
  542. {
  543.     time_t    StartDay;
  544.     time_t    FutureDay;
  545.  
  546.     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
  547.     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
  548.     return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60;
  549. }
  550.  
  551.  
  552. static time_t
  553. RelativeMonth(Start, RelMonth)
  554.     time_t    Start;
  555.     time_t    RelMonth;
  556. {
  557.     struct tm    *tm;
  558.     time_t    Month;
  559.     time_t    Year;
  560.  
  561.     tm = localtime(&Start);
  562.     Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
  563.     Year = Month / 12;
  564.     Month = Month % 12 + 1;
  565.     return DSTcorrect(Start,
  566.         Convert(Month, (time_t)tm->tm_mday, Year,
  567.         (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
  568.         MER24, DSTmaybe));
  569. }
  570.  
  571.  
  572. static int
  573. LookupWord(buff, length)
  574.     char        *buff;
  575.     register int    length;
  576. {
  577.     register char    *p;
  578.     register STRING    q;
  579.     register TABLE    *tp;
  580.     register int    c;
  581.  
  582.     p = buff;
  583.     c = p[0];
  584.  
  585.     /* See if we have an abbreviation for a month. */
  586.     if (length == 3 || (length == 4 && p[3] == '.'))
  587.     for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
  588.         q = tp->name;
  589.         if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
  590.         yylval.Number = tp->value;
  591.         return tp->type;
  592.         }
  593.     }
  594.     else
  595.     for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
  596.         if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  597.         yylval.Number = tp->value;
  598.         return tp->type;
  599.         }
  600.  
  601.     /* Try for a timezone. */
  602.     for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
  603.     if (c == tp->name[0] && p[1] == tp->name[1]
  604.      && strcmp(p, tp->name) == 0) {
  605.         yylval.Number = tp->value;
  606.         return tp->type;
  607.     }
  608.  
  609.     /* Try the units table. */
  610.     for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
  611.     if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  612.         yylval.Number = tp->value;
  613.         return tp->type;
  614.     }
  615.  
  616.     /* Strip off any plural and try the units table again. */
  617.     if (--length > 0 && p[length] == 's') {
  618.     p[length] = '\0';
  619.     for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
  620.         if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  621.         p[length] = 's';
  622.         yylval.Number = tp->value;
  623.         return tp->type;
  624.         }
  625.     p[length] = 's';
  626.     }
  627.     length++;
  628.  
  629.     /* Drop out any periods. */
  630.     for (p = buff, q = (STRING)buff; *q; q++)
  631.     if (*q != '.')
  632.         *p++ = *q;
  633.     *p = '\0';
  634.  
  635.     /* Try the meridians. */
  636.     if (buff[1] == 'm' && buff[2] == '\0') {
  637.     if (buff[0] == 'a') {
  638.         yylval.Meridian = MERam;
  639.         return tMERIDIAN;
  640.     }
  641.     if (buff[0] == 'p') {
  642.         yylval.Meridian = MERpm;
  643.         return tMERIDIAN;
  644.     }
  645.     }
  646.  
  647.     /* If we saw any periods, try the timezones again. */
  648.     if (p - buff != length) {
  649.     c = buff[0];
  650.     for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
  651.         if (c == tp->name[0] && p[1] == tp->name[1]
  652.         && strcmp(p, tp->name) == 0) {
  653.         yylval.Number = tp->value;
  654.         return tp->type;
  655.         }
  656.     }
  657.  
  658.     /* Unknown word -- assume GMT timezone. */
  659.     yylval.Number = 0;
  660.     return tZONE;
  661. }
  662.  
  663.  
  664. static int
  665. date_lex()
  666. {
  667.     register char    c;
  668.     register char    *p;
  669.     char        buff[20];
  670.     register int    sign;
  671.     register int    i;
  672.     register int    nesting;
  673.  
  674.     for ( ; ; ) {
  675.     /* Get first character after the whitespace. */
  676.     for ( ; ; ) {
  677.         while (CTYPE(isspace, *yyInput))
  678.         yyInput++;
  679.         c = *yyInput;
  680.  
  681.         /* Ignore RFC 822 comments, typically time zone names. */
  682.         if (c != LPAREN)
  683.         break;
  684.         for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
  685.         if (c == LPAREN)
  686.             nesting++;
  687.         else if (!IS7BIT(c) || c == '\0' || c == '\r'
  688.              || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
  689.             /* Lexical error: bad comment. */
  690.             return '?';
  691.         yyInput++;
  692.     }
  693.  
  694.     /* A number? */
  695.     if (CTYPE(isdigit, c) || c == '-' || c == '+') {
  696.         if (c == '-' || c == '+') {
  697.         sign = c == '-' ? -1 : 1;
  698.         yyInput++;
  699.         if (!CTYPE(isdigit, *yyInput))
  700.             /* Skip the plus or minus sign. */
  701.             continue;
  702.         }
  703.         else
  704.         sign = 0;
  705.         for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, c); )
  706.         i = 10 * i + c - '0';
  707.         yyInput--;
  708.         yylval.Number = sign < 0 ? -i : i;
  709.         return sign ? tSNUMBER : tUNUMBER;
  710.     }
  711.  
  712.     /* A word? */
  713.     if (CTYPE(isalpha, c)) {
  714.         for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, c); )
  715.         if (p < &buff[sizeof buff - 1])
  716.             *p++ = CTYPE(isupper, c) ? tolower(c) : c;
  717.         *p = '\0';
  718.         yyInput--;
  719.         return LookupWord(buff, p - buff);
  720.     }
  721.  
  722.     return *yyInput++;
  723.     }
  724. }
  725.  
  726.  
  727. time_t
  728. parsedate(p, now)
  729.     char        *p;
  730.     TIMEINFO        *now;
  731. {
  732.     extern int        date_parse();
  733.     struct tm        *tm;
  734.     TIMEINFO        ti;
  735.     time_t        Start;
  736.  
  737.     yyInput = p;
  738.     if (now == NULL) {
  739.     now = &ti;
  740.     (void)GetTimeInfo(&ti);
  741.     }
  742.  
  743.     tm = localtime(&now->time);
  744.     yyYear = tm->tm_year;
  745.     yyMonth = tm->tm_mon + 1;
  746.     yyDay = tm->tm_mday;
  747.     yyTimezone = now->tzone;
  748.     yyDSTmode = DSTmaybe;
  749.     yyHour = 0;
  750.     yyMinutes = 0;
  751.     yySeconds = 0;
  752.     yyMeridian = MER24;
  753.     yyRelSeconds = 0;
  754.     yyRelMonth = 0;
  755.     yyHaveDate = 0;
  756.     yyHaveRel = 0;
  757.     yyHaveTime = 0;
  758.  
  759.     if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
  760.     return -1;
  761.  
  762.     if (yyHaveDate || yyHaveTime) {
  763.     Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
  764.             yyMeridian, yyDSTmode);
  765.     if (Start < 0)
  766.         return -1;
  767.     }
  768.     else {
  769.     Start = now->time;
  770.     if (!yyHaveRel)
  771.         Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
  772.     }
  773.  
  774.     Start += yyRelSeconds;
  775.     if (yyRelMonth)
  776.     Start += RelativeMonth(Start, yyRelMonth);
  777.  
  778.     /* Have to do *something* with a legitimate -1 so it's distinguishable
  779.      * from the error return value.  (Alternately could set errno on error.) */
  780.     return Start == -1 ? 0 : Start;
  781. }
  782.  
  783.  
  784. #if    defined(TEST)
  785.  
  786. #if    YYDEBUG
  787. extern int    yydebug;
  788. #endif    /* YYDEBUG */
  789.  
  790. /* ARGSUSED */
  791. int
  792. main(ac, av)
  793.     int        ac;
  794.     char    *av[];
  795. {
  796.     char    buff[128];
  797.     time_t    d;
  798.  
  799. #if    YYDEBUG
  800.     yydebug = 1;
  801. #endif    /* YYDEBUG */
  802.  
  803.     (void)printf("Enter date, or blank line to exit.\n\t> ");
  804.     for ( ; ; ) {
  805.     (void)printf("\t> ");
  806.     (void)fflush(stdout);
  807.     if (gets(buff) == NULL || buff[0] == '\n')
  808.         break;
  809. #if    YYDEBUG
  810.     if (strcmp(buff, "yydebug") == 0) {
  811.         yydebug = !yydebug;
  812.         printf("yydebug = %s\n", yydebug ? "on" : "off");
  813.         continue;
  814.     }
  815. #endif    /* YYDEBUG */
  816.     d = parsedate(buff, (TIMEINFO *)NULL);
  817.     if (d == -1)
  818.         (void)printf("Bad format - couldn't convert.\n");
  819.     else
  820.         (void)printf("%s", ctime(&d));
  821.     }
  822.  
  823.     exit(0);
  824.     /* NOTREACHED */
  825. }
  826. #endif    /* defined(TEST) */
  827.